package sim.core;

import sim.gui.* ;
import sim.util.* ;
import sim.graphs.*;

import java.awt.*;
import java.util.*;
import java.awt.geom.*;

/**
 * @author smd
 *
 */
abstract public class Network implements Displayable {
    
	static Network Network ;
	
    public Network() {
    	Network = this ;
    }
    
    /**
     * Returns a set of the endpoints of the nodes in the network.
     * @return the set of the endpoints of the nodes in the network.
     */
    abstract public Set<EndPoint> endpoints() ;
    
    abstract public void disposeEndPoint( EndPoint x ) ;
    
    abstract public EndPoint newEndPoint( MessageHandler handler ) ;
    
    
    /**
     * Controls how the complete graph connecting all nodes will be simplified to create a cover graph (spanner)
     * @param T - Is the maximum cost degradation allowed between any pair of nodes in the simplified graph, 
     * relative to the cost in the original complete graph. Any value larger than 1 is allowed, the greater the
  
     */
    public void setSpannerThreshold( double T ) {
    	if( K != T && T > 1) {
    		K = (float) T ;
    		spanner = null ;
    		trees.clear();
    	}
    	if( T <= 1 ) throw new RuntimeException("Invalid Argument. Use a value greater than 1.") ;
    }
    
    
    
    /**
     * Given a node, chosen as the root node of a shortest paths tree, this method
     * returns the children nodes of a given node.
     * 
     *  The shortest paths tree is computed as needed from the spanner graph produced by the current SpannerThreshold
     *  value, which can be changed using setSpannerThreshold().
     * @param root - The root node of the shortest paths tree.
     * @param parent - A node in the network.
     * @return the children nodes of node selected as parent.
     */
    synchronized public Set<EndPoint> children( EndPoint root, EndPoint parent ) {
    	Set<EndPoint> res = shortestPathsTree( root ).children(parent) ;
    	assert res != null ;
    	return res ;
    }
	
  //------------------------------------------------------------------------------------------------------------------
    synchronized public void display(Graphics2D gu, Graphics2D gs) {

		gs.setColor( Color.gray ) ;
		gs.setStroke( new BasicStroke(0.15f)) ;
		for( Link<EndPoint> i : spanner().links() ) {
			gs.draw( new Line2D.Double( i.v.pos.x, i.v.pos.y, i.w.pos.x, i.w.pos.y) ) ;
		}
		
		XY mouse = Gui.Gui.getMouseXY() ;
		EndPoint closest = null ;
		for( EndPoint i : Network.endpoints() )
			if( closest == null || mouse.distance(i.pos) < mouse.distance(closest.pos) )
				closest = i ;
		
		gs.setColor( Color.darkGray ) ;
		gs.setStroke( new BasicStroke(5f)) ;
		if( closest != null && mouse.distance( closest.pos ) < 10 ) {
			for( Link<EndPoint> i : shortestPathsTree( closest ).edges() ) {
				gs.draw( new Line2D.Double( i.v.pos.x, i.v.pos.y, i.w.pos.x, i.w.pos.y) ) ;
			}
		}
    }
    
    synchronized ShortestPathsTree<EndPoint> shortestPathsTree( EndPoint root ) {
    	ShortestPathsTree<EndPoint> res = trees.get( root ) ;
    	if( res == null ) {
    		res = new ShortestPathsTree<EndPoint>( root, spanner() ) ;
    		trees.put( root, res ) ;
    	}
    	return res ;
	}

    synchronized private kSpanner<EndPoint> spanner() {
		if (spanner == null) {
			spanner = spanner(K) ;
		}
		return spanner;
	}

    synchronized private kSpanner<EndPoint> spanner(float f) {
		ArrayList<Link<EndPoint>> n2n = new ArrayList<Link<EndPoint>>();
		
		ArrayList<EndPoint> eal = new ArrayList<EndPoint>( endpoints() ) ;
		
		int N = eal.size() ;
		for (int i = 0; i < N; i++) {
			EndPoint eI = eal.get(i);
			for (int j = 0; j < i; j++) {
				EndPoint eJ = eal.get(j);
				n2n.add(new Link<EndPoint>(eI, eJ, (float) eI.distance(eJ)));
			}
		}
		return new kSpanner<EndPoint>(f, eal, n2n);
	}
	
    float K =4.001f ;
    final double Jitter = 2.50 ;
    private kSpanner<EndPoint> spanner = null;
    private Map<EndPoint, ShortestPathsTree<EndPoint>> trees = new HashMap<EndPoint, ShortestPathsTree<EndPoint>>() ;
}

